﻿using System;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Framework.Core.Dependency.Domain;

namespace Framework.Core.Dependency.Parser
{
    internal class CppProjectParser : IStreamParser<Project>
    {
        public Project Parse(Stream projectStream, string filePath, string branch)
        {
            using (var reader = XmlReader.Create(projectStream))
            {
                var project = XDocument.Load(reader);

                return ParseAll(project, filePath);
            }
        }

        private static Project ParseAll(XDocument p, string file)
        {
            var ns = GetNameSpace(p);
            var projectName = Path.GetFileNameWithoutExtension(file);
            var project = new Project(projectName, Path.GetExtension(file), file, projectName, Language.Cpp);

            ParseVcproj(p, ns, project);
            ParseVcxproj(p, ns, project, file);

            return project;
        }

        private static void ParseVcproj(XDocument p, XNamespace ns, Project project)
        {
            p.Descendants(ns + "File").ToList()
             .ForEach(
                 x =>
                     {
                         var filterName = (x.Parent.Attribute("Name") != null)
                                              ? x.Parent.Attribute("Name").Value
                                              : string.Empty;

                         if (filterName.Equals("Header Files", StringComparison.CurrentCultureIgnoreCase))
                         {
                             var path = x.Attribute("RelativePath").Value;
                             project.AddModule(new Module(Path.GetFileNameWithoutExtension(path), path));
                         }
                         else
                         {
                             project.Files.Add(x.Attribute("RelativePath").Value);
                         }
                     });
        }

        private static void ParseVcxproj(XContainer p, XNamespace ns, Project project, string file)
        {
            p.Descendants(ns + "ClCompile").ToList()
             .ForEach(
                 x =>
                     {
                         var includeAttribute = x.Attribute("Include");
                         if (includeAttribute != null)
                         {
                             project.Files.Add(includeAttribute.Value);
                         }
                     });

            p.Descendants(ns + "Import").ToList()
             .ForEach(
                 x =>
                     {
                         var importProjectAttribute = x.Attribute("Project");
                         if (importProjectAttribute == null)
                         {
                             return;
                         }

                         var fileProject = importProjectAttribute.Value;
                         var fullFileProjectPath =
                             Path.GetFullPath(
                                 Path.Combine(new FileInfo(file).Directory.FullName, fileProject)
                                     .Replace("$(Configuration)", "Release"));

                         if (File.Exists(fullFileProjectPath))
                         {
                             project.Files.Add(fullFileProjectPath);
                         }
                     });

            p.Descendants(ns + "ClInclude").ToList()
             .ForEach(
                 x =>
                     {
                         // ReSharper disable PossibleNullReferenceException
                         var path = x.Attribute("Include").Value;
                         var include = Path.GetFileNameWithoutExtension(path);
                         // ReSharper restore PossibleNullReferenceException
                         project.AddModule(new Module(include, path));
                     });
        }

        private static XNamespace GetNameSpace(XDocument project)
        {
            // ReSharper disable PossibleNullReferenceException
            var nsAttribute = project.Root.Attribute("xmlns");
            // ReSharper restore PossibleNullReferenceException

            if (nsAttribute == null)
            {
                return string.Empty;
            }

            var ns = XNamespace.Get(nsAttribute.Value);
            return ns;
        }
    }
}